/*
 * @Author: Suo-Yuheng syh15399227325@163.com
 * @Date: 2025-08-24 15:07:15
 * @LastEditors: Suo-Yuheng syh15399227325@163.com
 * @LastEditTime: 2025-08-24 18:23:00
 * @FilePath: /0824/pipe_use/processPool.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include"Task.hpp"

const int processNum = 10;
std::vector<task_t> tasks;

// 先描述
class channel
{
public:
    channel(int cmdfd, int slaverid, const std::string& processname)
        :_cmdfd(cmdfd)
        ,_slaverid(slaverid)
        ,_processname(processname)
    {}

    
    int _cmdfd;  // 父进程 用来发送任务的 文件描述符
    pid_t _slaverid; // 子进程的 pid
    std::string _processname;    // 子进程的 名字 —— 方便打印日志

};

void slaver()
{
    // read(0); 直接从标准输入读就 ok
    while(true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int)); // 固定地 从管道中读 4 byte，如果 父进程没有给 子进程发送数据？阻塞
        if(n == sizeof(int))
        {
            // 执行 cmdcode 对应地 任务列表
            std::cout << "slaver say@ get a command: "<< getpid() << " : cmdcode: " << cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size())    
                tasks[cmdcode]();
        }
        if(n == 0)  // 父进程 管道 写端关闭，子进程 read 读到文件末尾 0
        {
            break;
        }
    }
}


// 输入 ： const &
// 输出 ： *
// 输入输出　：　＆
void InitProcessPool(std::vector<channel>* channels)    // 输出型 参数
{
    // 确保 每一个子进程 都只有 一个写端
    std::vector<int> oldfds;

    for (size_t i = 0; i < processNum; i++)
    {
        int pipefd[2];  // 临时空间
        int n = pipe(pipefd);   // 创建管道、
        assert(!n); // n必须==0
        (void)n;

        pid_t id = fork();
        if(id == 0)
        {
            // child
            
            std::cout << "child: " << getpid() << " close history fd: ";
            for(auto fd : oldfds)   // 关闭上次继承的 写端
            {
                std::cout << fd << " ";
                close(fd);
            }
            std::cout << std::endl;

            close(pipefd[1]);   // 关闭本次继承的 写端
            dup2(pipefd[0], 0); // 重定向：将子进程的 标准输入 0号，替换成 管道的 读端
            slaver();   // 不用传参了
            // slaver(pipefd[0]);
            
            std::cout << " process : " << getpid() << " quit " << std::endl;
            exit(0);
        }

        // father
        close(pipefd[0]);   // 关闭读端

        // 至此，已经建立了 父子进程的 单向信道
        // 添加 channel 的字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));
        oldfds.push_back(pipefd[1]);    // 保存父进程 这次创建的 写端，循环创建子进程时，进行关闭
        sleep(1);
    }

}

void test(const std::vector<channel>& channels)
{
    for(const auto& c : channels)
    {
        std::cout << c._cmdfd << " " << c._slaverid  << " " << c._processname << std::endl;
    }

}

void Menu()
{
    std::cout << "#############################################" << std::endl;
    std::cout << "############## 1.LOL 刷新日志 ################" << std::endl;
    std::cout << "############## 2.LOL 刷新蓝量 ################" << std::endl;
    std::cout << "############## 3.LOL 刷新血量 ################" << std::endl;
    std::cout << "############## 4.LOL 刷新CDCD ################" << std::endl;
    std::cout << "############### 0. exit  #####################" << std::endl;
    std::cout << "#############################################" << std::endl;

}

void ctrlSlaver(const std::vector<channel>& channels)
{
    int which = 0;  // 轮转选择 子进程 以执行任务
    // int cnt = 5;
    

    while(true)
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@ ";
        std::cin >> select;
        if(select <=0 || select >= 5)
            break;
        
        // 1、选择任务
        // int cmdcode = rand() % tasks.size();
        int cmdcode = select-1;

        // 2、选择进程、、负载均衡 (随机数、轮转)
        // int processpos = rand()%channels.size();

        std::cout << "father say: cmdcode: " << cmdcode << " already send to: " << channels[which]._slaverid 
        << " processname: " << channels[which]._processname << std::endl;
        // std::cout << "father say: cmdcode: " << cmdcode << " already send to: " << channels[processpos]._slaverid 
        //     << " processname: " << channels[processpos]._processname << std::endl;
        
        // 3、发送任务
        // write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode));
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        which++;
        which %= channels.size();
        // sleep(1);
    }

}

void quitProcess(const std::vector<channel>& channels)
{
    // version 1
    // int last = channels.size();
    // for (size_t i = last; i > 0; i--)
    // {
    //     close(channels[i]._cmdfd);  // 关闭
    //     waitpid(channels[i]._slaverid, nullptr, 0);
    // }

    // version 2
    for (const auto& c : channels)
    {
        close(c._cmdfd);  // 关闭
        waitpid(c._slaverid, nullptr, 0);
    }

    // for(const auto& c : channels)
    // {
    //     close(c._cmdfd);
    // }
    // // sleep(5);
    // for(const auto& c : channels)
    // {
    //     waitpid(c._slaverid, nullptr, 0);
    // }
    // // sleep(5);
}

int main()
{
    
    LoadTask(&tasks);   // load 任务对象，子进程是 可以看到

    srand(time(nullptr)^getpid()^1023); // 种一个随机数种子
    // 再组织
    std::vector<channel> channels;
    // 1、初始化
    InitProcessPool(&channels);
    
    // test
    // test(channels);
    
    // 2、开始控制子进程
    ctrlSlaver(channels);

    // 3、清理收尾
    quitProcess(channels);

    // sleep(1000);
    return 0;
}